package org.ccay.core.orm.impl;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.ccay.core.exception.ApplicationException;
import org.ccay.core.exception.impl.CommonApplicationException;
import org.ccay.core.exception.impl.CommonSystemException;
import org.ccay.core.ioc.Ccay;
import org.ccay.core.request.Application;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;


public class ReloadDaoUtil{
	public static synchronized void rescanDao() throws ApplicationException {
		if (!Application.getCurrent().getEnviorment().equals("dev")) {
			throw new CommonApplicationException(
					"This function can be used only in development environment.");
		}

		Configuration configuration = Ccay.getContext().getBean("sqlSessionFactory", SqlSessionFactory.class)
				.getConfiguration();

		try {
			Field mapperRegistry = Configuration.class.getDeclaredField("mapperRegistry");
			// 不让Java语言检查访问修饰符
			mapperRegistry.setAccessible(true);
			MapperRegistry mapperRegistryValue = (MapperRegistry) mapperRegistry.get(configuration);
			// 通过反射清空Configuration中的一些私有集合，以便重新装载
			cleanCollcetion(configuration, "loadedResources");
			cleanCollcetion(configuration, "sqlFragments");
			cleanCollcetion(configuration, "incompleteStatements");
			cleanCollcetion(configuration, "incompleteCacheRefs");
			cleanCollcetion(configuration, "incompleteResultMaps");
			cleanCollcetion(configuration, "mappedStatements");
			cleanCollcetion(configuration, "keyGenerators");
			cleanCollcetion(configuration, "parameterMaps");
			cleanCollcetion(configuration, "resultMaps");
			
			
			MapperScannerConfigurer config = Ccay.getContext().getBean(MapperScannerConfigurer.class);
			Field field = config.getClass().getDeclaredField("basePackage");
			field.setAccessible(true);
			String basePackage = (String) field.get(config);
			
			String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			
			for (String basePackageItem : basePackages) {
				Resource[] resources = getResource(basePackageItem);
				if (resources != null) {
					for (int i = 0; i < resources.length; i++) {
						if (resources[i] == null) {
							continue;
						}
						try {
							XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resources[i].getInputStream(),
									configuration, resources[i].toString(), configuration.getSqlFragments());
							xmlMapperBuilder.parse();
						} catch (Exception e) {
							throw new NestedIOException("Failed to parse mapping resource: '" + resources[i] + "'", e);
						} finally {
							ErrorContext.instance().reset();
						}
					}
				}
				
			}
			// 支持mysql和oracle版本重加获取xml所在包end modify by zwx304259
		} catch (Exception e) {
			throw new CommonSystemException("Unable to reload mapper cache.", e);
		}
	}

	
	private static void cleanCollcetion(Object o, String field) throws IllegalArgumentException, IllegalAccessException,SecurityException, NoSuchFieldException {
		Field fieldRef = o.getClass().getDeclaredField(field);
		fieldRef.setAccessible(true);
		Object fieldValue = fieldRef.get(o);
		if (fieldValue instanceof Map) {
			((Map) fieldValue).clear();
		} else if (fieldValue instanceof Collection) {
			((Collection) fieldValue).clear();
		}
	}
	
	private static Resource[] getResourceByPattern(String basePackage, String pattern) throws IOException {
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
				+ ClassUtils.convertClassNameToResourcePath(Ccay.getContext().getEnvironment().resolveRequiredPlaceholders(
						basePackage)) + "/" + pattern;
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
		return resources;
	}
	
	public static Resource[] getResource(String basePackage) throws IOException {
		Map<String,Resource> typeResourceMap = new HashMap<String,Resource>();
		Resource[] daoResource = getResourceByPattern(basePackage,"**/*Dao.xml");
		for(Resource r:daoResource){
			if(null == r ){
				continue;
			}
			typeResourceMap.put(r.getDescription().replace(".xml", ""), r);
		}
		
		Resource[] overDaoResource = getResourceByPattern(basePackage,"**/*Dao.overrides.xml");
		for(Resource r:overDaoResource){
			if(null == r ){
				continue;
			}
			String description = r.getDescription();
			description = description.replace("\\", "/");
			typeResourceMap.put(r.getDescription().replace(".overrides.xml", ""), r);
		}
		return typeResourceMap.values().toArray(new Resource[0]);
	}
}